/**
 * Copyright (C), 2015-2019, XXX有限公司
 * FileName: ZHDispatcherServlet
 * Author:   zhouheng
 * Date:     2019/5/19 22:56
 * Description: 前端控制器
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.zhouh.springmvc.com.zhouh.mvc.framework.servlet;

import com.zhouh.springmvc.com.zhouh.mvc.framework.annotation.ZHController;
import com.zhouh.springmvc.com.zhouh.mvc.framework.annotation.ZHRequestMapping;
import com.zhouh.springmvc.com.zhouh.mvc.framework.annotation.ZHRequestParam;
import com.zhouh.springmvc.com.zhouh.mvc.framework.context.ZHApplicationContext;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 〈一句话功能简述〉<br> 
 * 〈前端控制器〉
 *
 * @author zhouheng
 * @create 2019/5/19
 * @since 1.0.0
 */
public class ZHDispatcherServlet extends HttpServlet{
    private List<Handler> handlerMapping = new ArrayList<>();
    private Map<Handler,HandlerAdapter> adapterMapping = new HashMap<>();
    private List<ViewResolver> viewResolvers = new ArrayList<>();

    /**
     * @description: 初始化工作
     * @date: 2019/5/19 23:40
     * @author:zhouheng
     */
    @Override
    public void init() throws ServletException {
        // 初始化ioc容器
        ZHApplicationContext ioc = new ZHApplicationContext("");

        //请求解析
        initMultipartResolver(ioc);
        //多语言、国际化
        initLocaleResolver(ioc);
        //主题View层的
        initThemeResolver(ioc);

        // 解析url与method之间的关系
        initHandlerMappings(ioc);

        // 适配器（请求匹配全过程）
        initHandlerAdapters(ioc);

        //异常解析
        initHandlerExceptionResolvers(ioc);
        //视图转发（根据视图名字匹配到一个具体模板）
        initRequestToViewNameTranslator(ioc);
        //解析模板中的内容（拿到服务器传过来的数据，生成HTML代码）
        initViewResolvers(ioc);


    }
    /**
     * @description: 解析模板中的内容（拿到服务器传过来的数据，生成HTML代码）
     * @date: 2019/5/20 23:19
     * @author:zhouheng
     */
    private void initViewResolvers(ZHApplicationContext ioc) {
        //模板一般是不会放到WebRoot下的，而是放在WEB-INF下，或者classes下
        //这样就避免了用户直接请求到模板
        //加载模板的个数，存储到缓存中
        //检查模板中的语法错误

        String tempateRoot = ioc.getConfig().getProperty("tempateRoot");
        //归根到底就是一个文件，普通文件
        String rootPath = this.getClass().getClassLoader().getResource(tempateRoot).getFile();

        File dir = new File(rootPath);
        for (File file : dir.listFiles()){
            viewResolvers.add(new ViewResolver(file.getName(),file));
        }

    }

    /**
     * @description: 适配器（请求匹配全过程 handler -> view）
     * @date: 2019/5/20 22:23
     * @author:zhouheng
     */
    private void initHandlerAdapters(ZHApplicationContext context) {
        Map<String,Object> ioc = context.getAll();
        if(handlerMapping.isEmpty()){ return;}

        //参数类型作为key，参数的索引号作为值
        Map<String,Integer> paramMapping = new HashMap<String,Integer>();

        //只需要取出来具体的某个方法
        for (Handler handler : handlerMapping){
            //把这个方法上面所有的参数全部获取到
            Class<?>[] parameterTypes =  handler.method.getParameterTypes();

            //有顺序，但是通过反射，没法拿到我们参数名字
            //这里是匹配Request和Response
            for (int i = 0; i < parameterTypes.length; i++){
                if (parameterTypes[i] ==  (HttpServletRequest.class)
                || parameterTypes[i] ==  (HttpServletResponse.class)){
                    paramMapping.put(parameterTypes[i].getTypeName(),i);
                }
            }

            //匹配自定参数列表
            Annotation[][] pa = handler.method.getParameterAnnotations();
            for (int i = 0; i < pa.length; i ++) {
                for(Annotation a : pa[i]){
                    if(a instanceof ZHRequestParam){
                        String paramName = ((ZHRequestParam) a).value();
                        if(!"".equals(paramName.trim())){
                            paramMapping.put(paramName, i);
                        }
                    }
                }
            }

            adapterMapping.put(handler, new HandlerAdapter(paramMapping));
        }
    }


    // 请求解析 模仿spring
    private void initMultipartResolver(ZHApplicationContext context){}
    // 初始化多语言、国际化 模仿spring
    private void initLocaleResolver(ZHApplicationContext context){}
    // 初始化主题View层 模仿spring
    private void initThemeResolver(ZHApplicationContext context){}

    //异常解析
    private void initHandlerExceptionResolvers(ZHApplicationContext context){}
    //视图转发（根据视图名字匹配到一个具体模板）
    private void initRequestToViewNameTranslator(ZHApplicationContext context){}

    /**
     * @description: 解析url与method之间的关系
     * @date: 2019/5/20 21:23
     * @author:zhouheng
     */
    private void initHandlerMappings(ZHApplicationContext context) {
        Map<String,Object> ioc = context.getAll();
        String url = "";
        Class<?> clazz = null;
        if(ioc.isEmpty()){
            return;
        }

        //只要是由Cotroller修饰类，里面方法全部找出来
        //而且这个方法上应该要加了RequestMaping注解，如果没加这个注解，这个方法是不能被外界来访问的
        for (Map.Entry<String,Object> entry : ioc.entrySet()){
            clazz = entry.getValue().getClass();

            if (!clazz.isAnnotationPresent(ZHController.class)){
                continue;
            }
            if (clazz.isAnnotationPresent(ZHRequestMapping.class)) {
                ZHRequestMapping zhRequestMapping = clazz.getAnnotation(ZHRequestMapping.class);
                url = zhRequestMapping.name();
            }

            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods){
                if (!method.isAnnotationPresent(ZHRequestMapping.class)){
                    continue;
                }
                ZHRequestMapping requestMapping = method.getAnnotation(ZHRequestMapping.class);

                // 根据url 截取方法参数
                String regex = (url + requestMapping.name()).replaceAll("/+", "/");
                Pattern pattern = Pattern.compile(regex);

                handlerMapping.add(new Handler(method,entry.getValue(),pattern));
                System.out.println("Mapping: " + regex + " " +  method.toString());
            }
        }

    }


    /**
     * @description: 派发器
     * @date: 2019/5/19 23:38
     * @author:zhouheng
     */
    public void dispather(HttpServletRequest rest, HttpServletResponse resp){

    }

    /**
     * @description: 获取handler
     * @date: 2019/5/19 23:38
     * @author:zhouheng
     */
    public Handler getHandler(){
        return null;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        dispather(req,resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req,resp);
    }

    public static class ViewResolver{
        private String viewName;
        private File file;

        protected ViewResolver(String viewName,File file){
            this.viewName = viewName;
            this.file = file;
        }

        protected String parse(ZHModelAndView mv) throws Exception{

            StringBuffer sb = new StringBuffer();

            RandomAccessFile ra = new RandomAccessFile(this.file, "r");

            try{
                //模板框架的语法是非常复杂，但是，原理是一样的
                //无非都是用正则表达式来处理字符串而已
                //就这么简单，不要认为这个模板框架的语法是有多么的高大上
                //来我现在来做一个最接地气的模板，也就是咕泡学院独创的模板语法
                String line = null;
                while(null != (line = ra.readLine())){
                    Matcher m = matcher(line);
                    while (m.find()) {
                        for (int i = 1; i <= m.groupCount(); i ++) {
                            String paramName = m.group(i);
                            Object paramValue = mv.getModel().get(paramName);
                            if(null == paramValue){ continue; }
                            line = line.replaceAll("@\\{" + paramName + "\\}", paramValue.toString());
                        }
                    }

                    sb.append(line);
                }
            }finally{
                ra.close();
            }
            return sb.toString();
        }

        private Matcher matcher(String str){
            Pattern pattern = Pattern.compile("@\\{(.+?)\\}",Pattern.CASE_INSENSITIVE);
            Matcher m = pattern.matcher(str);
            return m;
        }


        public String getViewName() {
            return viewName;
        }

    }
    
    public static class Handler{
        public Method method;
        public Object controller;
        public Pattern pattern;


        public Handler(Method method, Object controller, Pattern pattern) {
            this.method = method;
            this.controller = controller;
            this.pattern = pattern;
        }
    }

    public static class HandlerAdapter{
        private Map<String,Integer> paramMapping;

        public HandlerAdapter(Map<String, Integer> paramMapping) {
            this.paramMapping = paramMapping;
        }



    }
}